
package com.jf.cloud.product.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.jf.cloud.api.auth.constant.SysTypeEnum;
import com.jf.cloud.api.feign.SearchSpuFeignClient;
import com.jf.cloud.api.leaf.feign.SegmentFeignClient;
import com.jf.cloud.common.constant.Constant;
import com.jf.cloud.common.constant.DistributedIdKey;
import com.jf.cloud.common.constant.StatusEnum;
import com.jf.cloud.common.database.dto.PageDTO;
import com.jf.cloud.common.database.vo.PageVO;
import com.jf.cloud.common.exception.LuckException;
import com.jf.cloud.common.mongodb.util.MongoPageUtil;
import com.jf.cloud.common.product.constant.SpuType;
import com.jf.cloud.common.product.vo.search.SpuSearchVO;
import com.jf.cloud.common.response.ResponseEnum;
import com.jf.cloud.common.response.ServerResponseEntity;
import com.jf.cloud.common.security.AuthUserContext;
import com.jf.cloud.product.bo.mongo.MongoSpuBrowseLogBO;
import com.jf.cloud.product.dto.SpuBrowseLogDTO;
import com.jf.cloud.product.mapper.SpuMapper;
import com.jf.cloud.product.model.SpuBrowseLog;
import com.jf.cloud.product.service.SpuBrowseLogService;
import com.jf.cloud.product.vo.SpuBrowseLogVO;
import com.jf.cloud.product.vo.mongo.MongoSpuBrowseLogVO;
import com.mongodb.client.result.UpdateResult;
import ma.glasnost.orika.MapperFacade;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 商品浏览记录表
 *
 * @author zz
 * @date 2021-03-19 14:28:14
 */
@Service
public class SpuBrowseLogServiceImpl implements SpuBrowseLogService {
    private final Logger logger = LoggerFactory.getLogger(SpuBrowseLogServiceImpl.class);

    @Autowired
    private SearchSpuFeignClient searchSpuFeignClient;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private MapperFacade mapperFacade;
    @Autowired
    private SegmentFeignClient segmentFeignClient;
    @Autowired
    private SpuMapper spuMapper;
    @Autowired
    private MongoPageUtil mongoPageUtil;

    @Override
    public PageVO<SpuBrowseLogVO> page(PageDTO pageDTO) {
        List<Long> selectSpuIds = spuMapper.listSpuIdsByExcludeStatus(StatusEnum.DELETE.value());
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(AuthUserContext.get().getUserId())
                .and("status").is(StatusEnum.ENABLE.value())
                .and("spuId").in(selectSpuIds));
        query.with(Sort.by(Sort.Direction.DESC,"updateTime"));
        PageVO<MongoSpuBrowseLogBO> mongoSpuBrowseLogPage = mongoPageUtil.doPage(MongoSpuBrowseLogBO.class, pageDTO, query);

        // 处理商品分页列表
        PageVO<SpuBrowseLogVO> pageVO = new PageVO<>();
        pageVO.setPages(mongoSpuBrowseLogPage.getPages());
        pageVO.setTotal(mongoSpuBrowseLogPage.getTotal());
        pageVO.setList(new ArrayList<>());
        if (CollUtil.isEmpty(mongoSpuBrowseLogPage.getList())) {
            return pageVO;
        }
        List<Long> spuIds = mongoSpuBrowseLogPage.getList().stream().map(MongoSpuBrowseLogBO::getSpuId).collect(Collectors.toList());
        ServerResponseEntity<List<SpuSearchVO>> spuResponse = searchSpuFeignClient.listSpuBySpuIds(spuIds);
        Map<Long, SpuSearchVO> spuMap = spuResponse.getData().stream().collect(Collectors.toMap(SpuSearchVO::getSpuId, s->s));
        for (MongoSpuBrowseLogBO mongoSpuBrowseLogBO : mongoSpuBrowseLogPage.getList()) {
            if (!spuMap.containsKey(mongoSpuBrowseLogBO.getSpuId())) {
                continue;
            }
            SpuBrowseLogVO spuBrowseLogVO = new SpuBrowseLogVO();
            spuBrowseLogVO.setSpuId(mongoSpuBrowseLogBO.getSpuId());
            SpuSearchVO spuSearchVO = spuMap.get(spuBrowseLogVO.getSpuId());
            spuBrowseLogVO.setSpuStatus(spuSearchVO.getSpuStatus());
            spuBrowseLogVO.setMainImgUrl(spuSearchVO.getMainImgUrl());
            spuBrowseLogVO.setSpuName(spuSearchVO.getSpuName());
            spuBrowseLogVO.setSpuType(spuSearchVO.getSpuType());
            spuBrowseLogVO.setScoreFee(spuSearchVO.getScoreFee());
            spuBrowseLogVO.setPriceFee(spuSearchVO.getPriceFee());
            spuBrowseLogVO.setBrowseTime(mongoSpuBrowseLogBO.getUpdateTime());
            spuBrowseLogVO.setSpuBrowseLogId(mongoSpuBrowseLogBO.getSpuBrowseLogId());
            pageVO.getList().add(spuBrowseLogVO);
        }
        return pageVO;
    }

    @Override
    public void save(MongoSpuBrowseLogBO spuBrowseLog) {
        ServerResponseEntity<Long> response = segmentFeignClient.getSegmentId(DistributedIdKey.MALL4CLOUD_SPU_BROWSE_LOG);
        if (!response.isSuccess()) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
        Long spuBrowseLogId = response.getData();
        spuBrowseLog.setSpuBrowseLogId(spuBrowseLogId);
        spuBrowseLog.setCreateTime(new Date());
        spuBrowseLog.setUpdateTime(new Date());
        spuBrowseLog.setStatus(StatusEnum.ENABLE.value());
        mongoTemplate.insert(spuBrowseLog);
    }

    @Override
    public void updateStatus(MongoSpuBrowseLogBO spuBrowseLog) {
        Update update = new Update();
        update.set("status", spuBrowseLog.getStatus());
        update.set("updateTime", new Date());
        Query query = new Query();
        query.addCriteria(Criteria.where("spuBrowseLogId").is(spuBrowseLog.getSpuBrowseLogId()));
        if (Objects.nonNull(spuBrowseLog.getUserId())) {
            query.addCriteria(Criteria.where("userId").is(spuBrowseLog.getUserId()));
        }
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, MongoSpuBrowseLogBO.class);
        if (updateResult.getModifiedCount() < 1) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
    }

    @Override
    public void delete(SpuBrowseLogDTO spuBrowseLogDTO) {
        if (CollUtil.isEmpty(spuBrowseLogDTO.getSpuBrowseLogIds())) {
            return;
        }
        Long userId = AuthUserContext.get().getUserId();
        Query query = new Query();
        query.addCriteria(Criteria.where("spuBrowseLogId").in(spuBrowseLogDTO.getSpuBrowseLogIds())); //spuBrowseId
        query.addCriteria(Criteria.where("userId").is(userId));
        Update update = new Update();
        update.set("status", StatusEnum.DELETE.value());
        UpdateResult updateResult = mongoTemplate.updateMulti(query, update, MongoSpuBrowseLogBO.class);
        if (updateResult.getModifiedCount() < 1) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
    }

    @Override
    public Long recommendCategoryId(Integer spuType) {
        if (Objects.isNull(spuType)) {
            spuType = SpuType.NORMAL.value();
        }
        // 过滤
        Criteria criteria = Criteria.where("status").is(StatusEnum.ENABLE.value())
                .and("userId").is(AuthUserContext.get().getUserId())
                .and("spuType").is(spuType);
        // 更新时间倒序
        SortOperation updateTimeSort = Aggregation.sort(Sort.Direction.DESC, "updateTime");
        // 使用商品浏览记录进行推荐时，使用的数据数量
        LimitOperation limitOperation = Aggregation.limit(Constant.MAX_SPU_BROWSE_NUM);
        // 分组
        GroupOperation groupOperation = Aggregation.group("categoryId")
                .count().as("num")
                .last("categoryId").as("categoryId");
        // 分类数倒序
        SortOperation numSort = Aggregation.sort(Sort.Direction.DESC, "num");
        // 只获取一个结果
        LimitOperation one = Aggregation.limit(1);
        ProjectionOperation projectionOperation = Aggregation.project("num", "categoryId");
        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoSpuBrowseLogBO> typedAggregation = Aggregation.newAggregation(
                MongoSpuBrowseLogBO.class,
                Aggregation.match(criteria),
                updateTimeSort,
                limitOperation,
                groupOperation,
                numSort,
                one,
                projectionOperation
        );
        logger.info("recommendCategoryId()的mongodb查询语句： {}", typedAggregation);
        // 执行聚合操作
        AggregationResults<MongoSpuBrowseLogVO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoSpuBrowseLogVO.class);
        // 取出最终结果
        List<MongoSpuBrowseLogVO> spuBrowseLogList = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(spuBrowseLogList)) {
            return null;
        }
        return spuBrowseLogList.get(0).getCategoryId();
    }

    @Override
    public SpuBrowseLog getCurrentLogBySpuIdAndUserId(SpuBrowseLogDTO spuBrowseLogDTO) {
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(spuBrowseLogDTO.getUserId())
                .and("spuId").is(spuBrowseLogDTO.getSpuId())
                .and("spuType").is(spuBrowseLogDTO.getSpuType())
                .and("createTime").lte(new Date()));
        MongoSpuBrowseLogBO spuBrowseLogBO = mongoTemplate.findOne(query, MongoSpuBrowseLogBO.class);
        return mapperFacade.map(spuBrowseLogBO, SpuBrowseLog.class);
    }

    @Override
    public List<Long> recentBrowse(Long userId, Long shopId) {
        List<Long> spuIds = spuMapper.listSpuIdByShopIdsAndStatus(SysTypeEnum.MULTISHOP.value(), Collections.singletonList(shopId), null, StatusEnum.DELETE.value());
        Criteria criteria = new Criteria();
        criteria.and("status").is(StatusEnum.ENABLE.value())
                .and("userId").is(userId)
                .and("spuId").in(spuIds);
        SortOperation sortOperation = Aggregation.sort(Sort.Direction.DESC, "updateTime");
        LimitOperation limitOperation = Aggregation.limit(3);
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(criteria),
                sortOperation,
                limitOperation
        );
        logger.info("recentBrowse()的mongodb查询语句： {}", aggregation);
        AggregationResults<MongoSpuBrowseLogBO> aggregate = mongoTemplate.aggregate(aggregation, MongoSpuBrowseLogBO.class, MongoSpuBrowseLogBO.class);
        List<MongoSpuBrowseLogBO> spuBrowseLogList = aggregate.getMappedResults();
        if (CollUtil.isEmpty(spuBrowseLogList)) {
            return new ArrayList<>();
        }
        return spuBrowseLogList.stream().map(MongoSpuBrowseLogBO::getSpuId).collect(Collectors.toList());
    }
}
